package xq.gwt.ui.crud;

import xq.gwt.mvc.model.AbstractEntityModel;
import xq.gwt.mvc.model.BooleanPropertyModel;
import xq.gwt.mvc.model.CommandModel;
import xq.gwt.mvc.model.EntityModel;
import xq.gwt.mvc.model.IntegerPropertyModel;
import xq.gwt.mvc.model.PropertyChangedEvent;
import xq.gwt.mvc.model.PropertyChangedListener;
import xq.gwt.mvc.model.PropertyModel;
import xq.gwt.mvc.model.StringPropertyModel;

public class PagerModel extends AbstractEntityModel implements PropertyChangedListener{
	
	private static final long serialVersionUID = 8585677718182291635L;	
	protected IntegerPropertyModel offset;
	protected IntegerPropertyModel increment;
	protected IntegerPropertyModel totalRowCount;
	protected StringPropertyModel pageInfo;
	protected PropertyModel[] props;
	protected BooleanPropertyModel canMoveForward;
	protected BooleanPropertyModel canMoveBackward;
	protected CommandModel moveFirstCommand;
	protected CommandModel movePreviousCommand;
	protected CommandModel moveNextCommand;
	protected CommandModel moveLastCommand;
	protected CommandModel[] commands;
	protected PageMovedListener pageMovedListener;
	
	public PagerModel(){
		init();
	}
	
	private void init(){
		offset = new IntegerPropertyModel();
		offset.addPropertyChangedListener(this);
		increment = new IntegerPropertyModel();
		increment.addPropertyChangedListener(this);
		totalRowCount = new IntegerPropertyModel();
		totalRowCount.addPropertyChangedListener(this);		
		pageInfo = new StringPropertyModel();
		canMoveForward = new BooleanPropertyModel();
		canMoveBackward = new BooleanPropertyModel();
		
		moveFirstCommand = new MoveFirst();
		moveLastCommand = new MoveLast();
		moveNextCommand = new MoveNext();
		movePreviousCommand = new MovePrevious();
		
		updateCanMoveBackward();
		updateCanMoveForward();
		
		props = new PropertyModel[]{offset,increment,pageInfo};
		commands = new CommandModel[] {moveFirstCommand, moveLastCommand, moveNextCommand, movePreviousCommand};
	}
	
	@Override
	public String getTitle() {		
		return "Pager";
	}

	@Override
	public PropertyModel[] getProperties() {		
		return props;
	}

	@Override
	public CommandModel[] getCommands() {		
		return commands;
	}

	public IntegerPropertyModel getOffset() {
		return offset;
	}

	public IntegerPropertyModel getIncrement() {
		return increment;
	}

	public IntegerPropertyModel getTotalRowCount() {
		return totalRowCount;
	}

	public StringPropertyModel getPageInfo() {
		return pageInfo;
	}
	
	private void updateCanMoveForward(){
		Boolean canMoveForwardVal = false;
		Integer lastPage = getTotalRowCountVal() - getIncrementVal();
		if(getOffsetVal() < lastPage){
			canMoveForwardVal = true;
		}
		canMoveForward.setValue(canMoveForwardVal);		
	}
	
	private void updateCanMoveBackward(){
		Boolean canMoveBackwardVal = false;
		if(getOffsetVal() != 0){
			canMoveBackwardVal = true;
		}
		canMoveBackward.setValue(canMoveBackwardVal);
	}
	
	private void updatePageInfo(){
		Integer toRow = getOffsetVal() + getIncrementVal();
		if(toRow > getTotalRowCountVal()){
			toRow = getTotalRowCountVal();
		}
		String pageInfoVal = (getOffsetVal()+1) + "-" + toRow + " of " + getTotalRowCountVal();
		pageInfo.setValue(pageInfoVal);
	}
	
	protected int getOffsetVal(){
		return offset.getValue() != null ? offset.getValue() : 0;
	}
	
	protected int getIncrementVal(){
		return increment.getValue() != null ? increment.getValue() : 0;
	}
	
	protected int getTotalRowCountVal(){
		return totalRowCount.getValue() != null ? totalRowCount.getValue() : 0;
	}

	public boolean moveNext(){
		if(!canMoveForward.getValue()){
			return false;
		}
				
		offset.setValue(getOffsetVal() + getIncrementVal());		
		if(offset.getValue() > getTotalRowCountVal()){
			offset.setValue(getTotalRowCountVal());
		}
		
		notifyPageMoved();		
		return true;
	}
	
	public boolean movePrevious(){
		if(!canMoveBackward.getValue()){
			return false;
		}
		
		offset.setValue(getOffsetVal() - getIncrementVal());
		if(offset.getValue() < 0){
			offset.setValue(0);
		}
		
		notifyPageMoved();
		return true;
	}
	
	public void moveLast(){
		Integer lastPage = getTotalRowCountVal() - getIncrementVal();
		offset.setValue(lastPage);		
		notifyPageMoved();		
	}
	
	public void moveFirst(){
		offset.setValue(0);		
		notifyPageMoved();
	}
		
	public CommandModel getMoveFirstCommand() {
		return moveFirstCommand;
	}

	public CommandModel getMovePreviousCommand() {
		return movePreviousCommand;
	}

	public CommandModel getMoveNextCommand() {
		return moveNextCommand;
	}

	public CommandModel getMoveLastCommand() {
		return moveLastCommand;
	}

	private class MoveFirst implements CommandModel{		
		private static final long serialVersionUID = 1808959711334221801L;

		@Override
		public void execute() throws Exception {
			moveFirst();			
		}

		@Override
		public String getCaption() {			
			return "<<";
		}

		@Override
		public String getName() {			
			return "MoveFirst";
		}

		@Override
		public BooleanPropertyModel isEnabled() {			
			return canMoveBackward;
		}

		@Override
		public String getFeedback() {			
			return null;
		}
		
	}
	
	private class MovePrevious implements CommandModel{

		private static final long serialVersionUID = 2043044167690044646L;

		@Override
		public void execute() throws Exception {
			movePrevious();			
		}

		@Override
		public String getCaption() {			
			return "<";
		}

		@Override
		public String getName() {			
			return "MovePrevious";
		}

		@Override
		public BooleanPropertyModel isEnabled() {			
			return canMoveBackward;
		}

		@Override
		public String getFeedback() {			
			return null;
		}		
	}

	private class MoveNext implements CommandModel{
		private static final long serialVersionUID = -7472794758955221749L;

		@Override
		public void execute() throws Exception {
			moveNext();			
		}

		@Override
		public String getCaption() {			
			return ">";
		}

		@Override
		public String getName() {			
			return "MoveNext";
		}

		@Override
		public BooleanPropertyModel isEnabled() {			
			return canMoveForward;
		}

		@Override
		public String getFeedback() {			
			return null;
		}		
	}
	
	private class MoveLast implements CommandModel{		
		private static final long serialVersionUID = 8958259023599806007L;

		@Override
		public void execute() throws Exception {
			moveLast();			
		}

		@Override
		public String getCaption() {			
			return ">>";
		}

		@Override
		public String getName() {			
			return "MoveLast";
		}

		@Override
		public BooleanPropertyModel isEnabled() {			
			return canMoveForward;
		}

		@Override
		public String getFeedback() {			
			return null;
		}		
	}
	
	@Override
	public void propertyChanged(PropertyChangedEvent event) {
		if(event.getSource().equals(offset) || event.getSource().equals(totalRowCount) || event.getSource().equals(increment)){
			updateCanMoveBackward();
			updateCanMoveForward();
			updatePageInfo();
		}
		
		notifyEnityChanged();		
	}

	protected void notifyPageMoved(){
		if(this.pageMovedListener != null){
			this.pageMovedListener.pageMoved();
		}
	}
	
	public PageMovedListener getPageMovedListener() {
		return pageMovedListener;
	}

	public void setPageMovedListener(PageMovedListener pageMovedListener) {
		this.pageMovedListener = pageMovedListener;
	}

	@Override
	public EntityModel newInstance() {		
		return new PagerModel();
	}
	
	
}
